import Loki from "lokijs";
import path from "path";
import fs from "fs";
import LzStringWorkerAdapter from "./lzStringWorkerAdapter";

const { WEB_NODE_ENV = "prod" } = useRuntimeConfig().public;

const projectRoot = process.cwd();
// console.log("获取项目根目录", projectRoot);

/**
 * @description:  文件不存在,创建配置文件
 */
const createConfigFn = (configPath: string) => {
  const writeDate = {
    // 文件创建时间
    create: formatDate(new Date()),
    // 24 小时,lark 消息通知一次
    noticSuccessTime: null,
    // 默认开始,上报功能
    isEnable: true,
    // 数据库配置数据
    dbs: [],
  };

  try {
    creatContentToPathSync(configPath, JSON.stringify(writeDate, null, 2));
  } catch (error) {
    console.log("❌ 创建配置文件失败：", error);
    const writePath = path.resolve(projectRoot, ".output/public", "log", "");
    createDirectorySync(writePath);
    configPath = path.resolve(
      projectRoot,
      ".output/public",
      "log",
      "bdc-lokijs-config.json"
    );
    creatContentToPathSync(configPath, JSON.stringify(writeDate, null, 2));
  }
};

/**
 * @description: 获取配置文件数据
 * 1. 不存在就创建
 * @return {*} 配置数据,和 路径
 * @author: zhj1214
 */
export const getBDCConfig = () => {
  // 1. 获取配置路径
  let configPath = path.resolve(projectRoot, "", "", "bdc-lokijs-config.json");
  // 2. 是否存在 配置文件
  let isHas = false;
  try {
    isHas = fs.existsSync(configPath);
  } catch (error) {
    isHas = false;
  }
  // 不存在，就创建配置文件
  if (!isHas) {
    console.log("配置文件是否存在?", isHas ? "✅" : "❌");
    createConfigFn(configPath);
  }

  // 3. 读取配置文件
  let configString = "";
  let configData = null;
  try {
    configString = fs.readFileSync(configPath!, "utf8");
    configData = JSON.parse(configString);
  } catch (error) {
    console.log("配置文件，读取失败，一般是文件损坏，重新创建");
    createConfigFn(configPath);
    configString = fs.readFileSync(configPath!, "utf8");
    configData = JSON.parse(configString);
  }

  return { configData, configPath };
};

// 1. 读取配置文件
const { configData, configPath } = getBDCConfig();

// 2. 获取配置
let instance: any = null;
const randomNumber = Math.floor(Math.random() * 10000001); // Math.random()生成的是0到1之间（包含0但不包含1）的随机数，乘以10000001后取整，就能得到0到10000000的整数
let dbFiles: any = [];

// 3.1 同步读取当前目录的文件列表: 加载本地已有数据库配置
const loadLocalDb = () => {
  try {
    const files = fs.readdirSync(".");
    // 过滤出文件名以 '-database.db' 结尾的文件
    dbFiles = files.filter((file) => {
      return file.endsWith("-database.db");
    });
  } catch (err) {
    console.error("读取目录出错：", err);
  }
  if (dbFiles.length > 0) {
    console.log("找到以下以 '-database.db' 结尾的文件：");
    dbFiles.forEach((fileTemp: any) => {
      console.log(fileTemp);
      const newFilePath = path.join(process.cwd(), fileTemp);
      configData.dbs.push({
        path: newFilePath,
        name: fileTemp,
        updateTime: formatDate(new Date(Date.now() - 100 * 1000)), // 可以一个过期的时间,代表这些数据库都没有使用
        createTime: formatDate(new Date(Number(fileTemp.createTime))), // 创建时间
      });
    });
  }
};

const addDb = () => {
  const newFilePath = path.join(
    process.cwd(),
    `bdc-${randomNumber}-database.db`
  );
  console.log("创建新的:", `bdc-${randomNumber}-database.db`);
  return {
    path: newFilePath,
    name: `bdc-${randomNumber}-database.db`,
    updateTime: formatDate(new Date()),
    createTime: formatDate(new Date()), // 创建时间
  };
};

// 特殊场景: 加载本地数据库
if (configData.dbs && configData.dbs.length == 0) {
  loadLocalDb();
}

if (configData.dbs && configData.dbs.length > 0) {
  configData.dbs.forEach((e: any) => {
    if (Date.now() - new Date(e.updateTime).getTime() > 5000 && !instance) {
      instance = e;
      console.log("4.1 使用数据库:", e.name);
    }
  });

  // 在创建一个配置
  if (!instance) {
    instance = addDb();
    configData.dbs.push(instance);
  }
} else {
  instance = addDb();
  configData.dbs = [instance];
}
// 4.2 写入配置
creatContentToPathSync(configPath!, JSON.stringify(configData, null, 2));

// 5. 构建数据库文件的路径，将其保存在项目根目录的上一级
const dbFilePath = path.join(process.cwd(), instance?.name!);

// console.log("5. lokijs 中间件 中的地址:", dbFilePath);

// 6. 确保保存数据库的目录存在，不存在上级目录就创建目录
const dbDir = path.dirname(dbFilePath);
if (!fs.existsSync(dbDir)) {
  fs.mkdirSync(dbDir, { recursive: true });
}

// 7. 创建 LokiJS 数据库实例，启用自动保存
const updateDbTimeFn = () => {
  // 读取
  const { configData: configDataTemp } = getBDCConfig();
  if (configDataTemp.dbs && configDataTemp.dbs.length == 0) {
    const newFilePath = path.join(
      process.cwd(),
      `bdc-${randomNumber}-database.db`
    );
    instance = {
      path: newFilePath,
      name: instance.name,
      updateTime: formatDate(new Date()),
      createTime: formatDate(new Date()), // 创建时间
    };
    configDataTemp.dbs = [instance];
  } else if (configDataTemp.dbs && configDataTemp.dbs.length > 0) {
    // 更新
    let isUpdateOk = false;
    configDataTemp.dbs.forEach((e: any) => {
      if (e.name == instance.name) {
        isUpdateOk = true;
        e.updateTime = formatDate(new Date());
      }
    });

    // 在创建一个配置
    if (!isUpdateOk) {
      instance = {
        path: instance.path || instance.name,
        name: instance.name,
        updateTime: formatDate(new Date()),
        createTime: formatDate(new Date()), // 创建时间
      };
      configDataTemp.dbs.push(instance);
    }
    // 保存
    creatContentToPathSync(configPath, JSON.stringify(configDataTemp, null, 2));
  }
};

// 创建底层适配器，例如文件系统适配器

const fsAdapter = new Loki.LokiFsAdapter();
// const fsAdapter = new LokiFsAdapter();
// 创建自定义压缩适配器，包装底层适配器
const adapter = new LzStringWorkerAdapter({ adapter: fsAdapter });

const db = new Loki(dbFilePath, {
  adapter,
  persistenceMethod: "fs", // 在 Node.js 环境中可省略
  // 启用自动加载
  autoload: true,
  // 自动加载完成后的回调
  autoloadCallback: databaseInitialize,
  // 启用自动保存
  autosave: true,
  // 启用节流保存
  throttledSaves: true,
  // 自动保存间隔（毫秒）
  autosaveInterval: 10000, // 每 10 秒自动检测一次;指定的是检查数据库是否需要保存的时间间隔，而不是固定地在每个间隔执行保存操作。 // 由于是保存到单独的数据库,所以没有必要把时间设置那么短
  // 启用详细日志
  verbose: WEB_NODE_ENV != "prod",
  // 自动保存回调
  autosaveCallback: (err: any) => {
    console.log("📚 7. 服务端持久化, 开始自动保存;错误信息：", !!err);
    if (err) {
      console.log("7.2 lokijs.server 自动保存错误:", err);
    } else {
      updateDbTimeFn();
    }
  },
  // 启用压缩
  // normal ：默认的序列化方法，输出紧凑的 JSON 字符串。
  // pretty ：格式化的 JSON 字符串，带有缩进和换行，便于阅读和调试，但文件体积较大。
  // destructured ：将每个集合的数据分别序列化，从而优化加载性能，适用于大型数据库。
  serializationMethod: "destructured",
});

// // 批量操作完成，启用自动保存
// db.ignoreAutosave = true;

function databaseInitialize() {
  // 数据库加载完成后，可以执行一些初始化操作
  console.log("数据库加载完成");
}

// 添加自定义属性
db.dbPath = dbFilePath;

export default db;

/**
 * @description: 检查并删除过期数据
 * @return {*}
 * @author: Berry
 */
export function cleanExpiredData(collection: any) {
  const now = Date.now(); // 获取当前时间
  const expiredItems = collection.find({ expiresAt: { $lt: now } }); // 查找已过期的数据
  // 删除所有过期的数据
  expiredItems.forEach((item: any) => collection.remove(item));
}

/**
 * @description: 获取数据库，指定集合数据
 * @param {any} lokiSelf
 * @param {string} collectionName
 * @return {*}
 * @author: Berry
 */
export const getCollectionData = (lokiSelf: any, collectionName: string) => {
  let collection = lokiSelf.getCollection(collectionName);
  if (collection != null) {
    cleanExpiredData(collection); // 清理过期数据
    return JSON.parse(JSON.stringify(collection.data)); // 深拷贝
  } else {
    return [];
  }
};

/**
 * @description: 获取其他数据库的实例
 * @param {any} instanceList
 * @return {*} 数据
 * @author: zhj1214
 */
export const getOtherDbData = (
  instanceList: any,
  collectionName = "watchErrLog"
) => {
  return new Promise((resolve, reject) => {
    let arr: any = [];
    let selfList: any = [];
    let i = 0;

    // 创建底层适配器，例如文件系统适配器
    const fsAdapter = new Loki.LokiFsAdapter();
    // 创建自定义压缩适配器，包装底层适配器
    const adapter = new LzStringWorkerAdapter({ adapter: fsAdapter });

    if (instanceList.length > 0) {
      instanceList.forEach((d: any) => {
        const itemLoki = new Loki(d.path, {
          adapter,
          persistenceMethod: "fs", // 在 Node.js 环境中可省略
          // 启用自动加载
          autoload: true,
          // 自动加载完成后的回调
          autoloadCallback: () => {
            console.log(d.name, "数据库加载完成");
            selfList.push(itemLoki);
            if (collectionName) {
              setTimeout(() => {
                let tempData = getCollectionData(itemLoki, collectionName);
                if (tempData.length > 0) {
                  // 增加数据库标识,用于删除
                  tempData = tempData.map((el: any) => {
                    el.dbName = d.name;
                    return el;
                  });
                  arr = arr.concat(tempData || []);
                } else {
                  console.log(d.name, "数据库 空数据");
                }
                i += 1;
                if (i == instanceList.length) {
                  resolve({ selfList, data: arr });
                }
              }, 1000);
            } else {
              i += 1;
              if (i == instanceList.length) {
                resolve({ selfList, data: arr });
              }
            }
          },
          // 启用自动保存
          autosave: false,
          serializationMethod: "destructured",
        });
      });
    } else {
      resolve({ selfList, data: arr });
    }
  });
};
